package dao.base;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigInteger;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.hibernate.LockOptions;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Example.PropertySelector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;


@Transactional
public abstract class BaseDaoImpl<T> implements BaseDao<T> {

	private static final Logger log = LoggerFactory
			.getLogger(BaseDaoImpl.class);

	/**
	 * T的class
	 */
	private Class<T> clazz;
	/***
	 * T类型的名字
	 */
	private String TClassName;

	@Resource
	protected SessionFactory sessionFactory;

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	private Session getCurrentSession() {
		return sessionFactory.getCurrentSession();
	}

	// protected abstract String className() ;

	
	public void save(T instance) {
		log.debug("saving " + TClassName + " instance");
		
		long  savestart = System.currentTimeMillis();
		
		try {
			getCurrentSession().save(instance);
			log.debug("save successful");
		} catch (RuntimeException re) {
			log.error("save failed", re);
			throw re;
		}
		long  saveend = System.currentTimeMillis();
		log.info((" save(T instance)存储消耗消耗时间：" + (saveend -savestart)));
	}

	@SuppressWarnings("unchecked")
	
	public T findById(Serializable id) {
		log.debug("getting " + TClassName + " instance with id: " + id);
		try {
			T instance = (T) getCurrentSession().get(TClassName, id);
			return instance;
		} catch (RuntimeException re) {
			log.error("get failed", re);
			throw re;
		}
	}

	@SuppressWarnings("unchecked")
	
	public List<T> findByExample(T instance) {
		log.debug("finding " + TClassName + " instance by example");
		try {
			Example example = Example.create(instance) 
					.setPropertySelector(new NullStringSelector()); 
			
			List<T> results = getCurrentSession().createCriteria(TClassName)
					.add(example).list();
			log.debug("find by example successful, result size: "
					+ results.size());
			return results;
		} catch (RuntimeException re) {
			log.error("find by example failed", re);
			throw re;
		}
	}

	@SuppressWarnings("unchecked")
	
	public List<T> findByProperty(String propertyName, Object value) {
		log.debug("finding " + TClassName + " instance with property: "
				+ propertyName + ", value: " + value);
		try {
			String queryString = "from " + clazz.getSimpleName()
					+ " as model where model." + propertyName + "= ?";
			Query queryObject = getCurrentSession().createQuery(queryString);
			queryObject.setParameter(0, value);
			return queryObject.list();
		} catch (RuntimeException re) {
			log.error("find by property name failed", re);
			throw re;
		}
	}
	@SuppressWarnings("unchecked")
	
	public List<T> findByProperty(String propertyName, Object value, int rows) {
		log.debug("finding " + TClassName + " instance with property: "
				+ propertyName + ", value: " + value);
		try {
			String queryString = "from " + clazz.getSimpleName()
					+ " as model where model." + propertyName + "= ?";
			Query queryObject = getCurrentSession().createQuery(queryString);
			queryObject.setParameter(0, value);
			queryObject.setMaxResults(rows);
			return queryObject.list();
		} catch (RuntimeException re) {
			log.error("find by property name failed", re);
			throw re;
		}
	}

	@SuppressWarnings("unchecked")
	
	public List<T> findAll() {
		log.debug("finding all " + TClassName + " instances");
		try {
			String queryString = "from  " + clazz.getSimpleName();
			Query queryObject = getCurrentSession().createQuery(queryString);
			return queryObject.list();
		} catch (RuntimeException re) {
			log.error("find all failed", re);
			throw re;
		}
	}

	@SuppressWarnings("unchecked")
	
	public List<T> find(String hql, int rows) {
		log.debug("finding all " + TClassName + " instances");
		try {
			String queryString = "from  " + clazz.getSimpleName();
			Query queryObject = getCurrentSession().createQuery(queryString);
			queryObject.setMaxResults(rows);
			return queryObject.list();
		} catch (RuntimeException re) {
			log.error("find all failed", re);
			throw re;
		}
	}
	@SuppressWarnings("unchecked")
	
	public T merge(T instance) {
		log.debug("merging " + TClassName + " instance");
		try {
			T result = (T) getCurrentSession().merge(instance);
			log.debug("merge successful");
			return result;
		} catch (RuntimeException re) {
			log.error("merge failed", re);
			throw re;
		}
	}

	
	public void lock(T instance) {
		log.debug("attaching clean " + TClassName + " instance");
		try {
			getCurrentSession().buildLockRequest(LockOptions.NONE).lock(
					instance);
			log.debug("attach successful");
		} catch (RuntimeException re) {
			log.error("attach failed", re);
			throw re;
		}
	}
	@SuppressWarnings("unchecked")
	
	public List<T> find(String hql, int page, int rows) {
		Query q = getCurrentSession().createQuery(hql);
		return q.setFirstResult((page - 1) * rows).setMaxResults(rows).list();
	}
	@SuppressWarnings("unchecked")
	
	public T get(String hql) {
		Query q = getCurrentSession().createQuery(hql);
		T t = (T) q.uniqueResult();
		return t;
	}
	@SuppressWarnings("unchecked")
	
	public T get(String hql, Map<String, Object> params) {
		Query q = getCurrentSession().createQuery(hql);
		if ((params != null) && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		List<T> l = q.list();
		if ((l != null) && (l.size() > 0)) {
			return l.get(0);
		}
		return null;
	}

	
	public void delete(T instance) {
		log.debug("deleting User instance");
		try {
			getCurrentSession().delete(instance);
			log.debug("delete successful");
		} catch (RuntimeException re) {
			log.error("delete failed", re);
			throw re;
		}
	}

	
	public void update(T o) {
		if (o != null) {
			this.getCurrentSession().update(o);
		}
	}

	
	public void saveOrUpdate(T o) {
		if (o != null) {
			
			this.getCurrentSession().saveOrUpdate(o);
		}
	}
	@SuppressWarnings("unchecked")
	
	public List<T> find(String hql) {
		Query q = getCurrentSession().createQuery(hql);
		return q.list();
	}
	@SuppressWarnings("unchecked")
	
	public List<T> find(String hql, Map<String, Object> params) {
		Query q = getCurrentSession().createQuery(hql);
		if ((params != null) && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		return q.list();
	}
	@SuppressWarnings("unchecked")
	
	public List<T> find(String hql, Map<String, Object> params, int offset,
			int rows) {
		Query q = getCurrentSession().createQuery(hql);
		if ((params != null) && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		return q.setFirstResult(offset).setMaxResults(rows).list();
	}
	
	public void delete(Collection<T>  instance){
		for (T t : instance) {
			delete(t);
		}
		
	}
	
	public Long count(String hql) {
		Query q = getCurrentSession().createQuery(hql);
		return (Long) q.uniqueResult();
	}

	
	public Long count(String hql, Map<String, Object> params) {
		Query q = getCurrentSession().createQuery(hql);
		if ((params != null) && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		return (Long) q.uniqueResult();
	}

	
	public int executeUpdateHql(String hql) {
		Query q = getCurrentSession().createQuery(hql);
		return q.executeUpdate();
	}

	@SuppressWarnings("unchecked")
	
	public List<T> exeHql(String hql) {
		Query q = getCurrentSession().createQuery(hql);
		return q.list();
	}
	@SuppressWarnings("unchecked")
	
	public List<T> exeSql(String sql) {
		SQLQuery q = getCurrentSession().createSQLQuery(sql);
		return q.list();
	}
	
	
	public int executeUpdateHql(String hql, Map<String, Object> params) {
		Query q = getCurrentSession().createQuery(hql);
		if ((params != null) && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		return q.executeUpdate();
	}
	@SuppressWarnings("unchecked")
	
	public List<Object[]> findBySql(String sql) {
		SQLQuery q = getCurrentSession().createSQLQuery(sql);
		return q.list();
	}
	@SuppressWarnings("unchecked")
	
	public List<Object[]> findBySql(String sql, int offset, int rows) {
		SQLQuery q = getCurrentSession().createSQLQuery(sql);
		return q.setFirstResult(offset).setMaxResults(rows).list();
	}
	@SuppressWarnings("unchecked")
	
	public List<Object[]> findBySql(String sql, Map<String, Object> params) {
		SQLQuery q = getCurrentSession().createSQLQuery(sql);
		if ((params != null) && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		return q.list();
	}
	@SuppressWarnings("unchecked")
	
	public List<Object[]> findBySql(String sql, Map<String, Object> params,
			int offset, int rows) {
		SQLQuery q = getCurrentSession().createSQLQuery(sql);
		if ((params != null) && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		return q.setFirstResult(offset).setMaxResults(rows).list();
	}

	
	public int executeSql(String sql) {
		SQLQuery q = getCurrentSession().createSQLQuery(sql);
		return q.executeUpdate();
	}

	
	public int executeSql(String sql, Map<String, Object> params) {
		SQLQuery q = getCurrentSession().createSQLQuery(sql);
		if ((params != null) && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		return q.executeUpdate();
	}

	
	public BigInteger countBySql(String sql) {
		SQLQuery q = getCurrentSession().createSQLQuery(sql);
		return (BigInteger) q.uniqueResult();
	}

	
	public BigInteger countBySql(String sql, Map<String, Object> params) {
		SQLQuery q = getCurrentSession().createSQLQuery(sql);
		if ((params != null) && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		return (BigInteger) q.uniqueResult();
	}
	@SuppressWarnings("unchecked")
	
	public List<Object[]> findUnionByHql(String hql) {
		Query q = getCurrentSession().createQuery(hql);
		return q.list();
	}
	@SuppressWarnings("unchecked")
	
	public List<Object[]> findUnionByHql(String hql, int page, int rows) {
		Query q = getCurrentSession().createQuery(hql);
		return q.setFirstResult((page - 1) * rows).setMaxResults(rows).list();
	}
	@SuppressWarnings("unchecked")
	
	public List<Object[]> findUnionByHql(String hql, Map<String, Object> params) {
		Query q = getCurrentSession().createQuery(hql);
		if ((params != null) && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		return q.list();
	}
	
	
	public void deleteAndSave(Collection<T> collection) {
		for (T instance : collection) {
			delete(instance);
			save(instance);
		}
	}
	
	
	public void deleteAndSave(T instance) {
		delete(instance);
		save(instance);
		
	}
	
	@SuppressWarnings("unchecked")
	
	public List<Object[]> findUnionByHql(String hql,
			Map<String, Object> params, int offset, int rows) {
		Query q = getCurrentSession().createQuery(hql);
		if ((params != null) && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		q.setFirstResult(offset).setMaxResults(rows);
		return q.list();
	}
	@SuppressWarnings("unchecked")
	
	public List<Object[]> findUnionByHql(String hql, List<String> list) {
		try {

			Query queryObject = getCurrentSession().createQuery(hql);
			for (int i = 0; i < list.size(); i++) {
				queryObject.setParameter(i, list.get(i));
			}

			return queryObject.list();
		} catch (RuntimeException re) {
			log.error("find by property name failed", re);
			throw re;
		}
	}
	@SuppressWarnings("unchecked")
	
	public List<Object[]> findUnionByHql(String hql, List<String> list,
			int offset, int rows) {
		try {

			Query queryObject = getCurrentSession().createQuery(hql);
			for (int i = 0; i < list.size(); i++) {
				queryObject.setParameter(i, list.get(i));
			}
			queryObject.setFirstResult(offset).setMaxResults(rows);
			return queryObject.list();
		} catch (RuntimeException re) {
			throw re;
		}
	}



	
	public void save(Collection<T> collection) {
		try {
			for (T instance : collection) {
				getCurrentSession().save(instance);
			}
		} catch (RuntimeException re) {
			log.error("批量保存失败", re);
			throw re;
		}
	}

	
	public void saveOrUpdate(Collection<T> collection) {

		try {
			for (T instance : collection) {
				getCurrentSession().saveOrUpdate(instance);
			}
		} catch (RuntimeException re) {
			log.error("批量保存失败", re);
			throw re;
		}

	}
	@SuppressWarnings("unchecked")
	
	public List<T> findWithCache(String hql, Map<String, Object> params) {

		Query q = getCurrentSession().createQuery(hql).setCacheable(true);
		if ((params != null) && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		return q.list();
	
	}
	@SuppressWarnings("unchecked")
	
	public List<T> findWithCache(String hql, Map<String, Object> params,
			int offset, int rows) {
		Query q = getCurrentSession().createQuery(hql).setCacheable(true);
		if ((params != null) && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		return q.setFirstResult(offset).setMaxResults(rows).list();
	}
	@SuppressWarnings("unchecked")
	
	public T loadById(Serializable id) {
		log.debug("getting " + TClassName + " instance with id: " + id);
		try {
			T instance = (T) getCurrentSession().load(TClassName, id);
			return instance;
		} catch (RuntimeException re) {
			log.error("get failed", re);
			throw re;
		}
	}
	@SuppressWarnings("unchecked")
	
	public List<T> findWithCache(String hql) {
		Query q = getCurrentSession().createQuery(hql).setCacheable(true);
		return q.list();
	}
	
	@SuppressWarnings("unchecked")
	@PostConstruct
	protected void setT() {
		Type genType = this.getClass().getGenericSuperclass();
		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
		// return clazz;
		clazz = (Class<T>) params[0];
		TClassName = clazz.getName();
		log.info(clazz.getName() + "开始初始化！");
	}

}
